package com.gitee.wsl.prism4j.languages

import com.gitee.wsl.prism4j.GrammarUtils
import com.gitee.wsl.prism4j.Prism4j
import com.gitee.wsl.prism4j.Prism4j.Companion.grammar
import com.gitee.wsl.prism4j.Prism4j.Companion.pattern
import com.gitee.wsl.prism4j.Prism4j.Companion.token
import com.gitee.wsl.prism4j.Prism4j.Pattern
import com.gitee.wsl.prism4j.annotations.Modify


@Suppress("unused")
@Modify("markup")
object Prism_css {
    // todo: really important one..
    // before a language is requested (fro example css)
    // it won't be initialized (so we won't modify markup to highlight css) before it was requested...
    fun create(prism4j: Prism4j): Prism4j.Grammar {
        val grammar: Prism4j.Grammar = grammar(
            "css",
            token("comment", pattern(Pattern.compile("\\/\\*[\\s\\S]*?\\*\\/"))),
            token(
                "atrule",
                pattern(
                    Pattern.compile(
                        "@[\\w-]+?.*?(?:;|(?=\\s*\\{))",
                        Pattern.CASE_INSENSITIVE
                    ),
                    false,
                    false,
                    null,
                    grammar(
                        "inside",
                        token("rule", pattern(Pattern.compile("@[\\w-]+")))
                    )
                )
            ),
            token(
                "url",
                pattern(
                    Pattern.compile(
                        "url\\((?:([\"'])(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\1)[^\\\\\\r\\n])*\\1|.*?)\\)",
                        Pattern.CASE_INSENSITIVE
                    )
                )
            ),
            token("selector", pattern(Pattern.compile("[^{}\\s][^{};]*?(?=\\s*\\{)"))),
            token(
                "string",
                pattern(
                    Pattern.compile("(\"|')(?:\\\\(?:\\r\\n|[\\s\\S])|(?!\\1)[^\\\\\\r\\n])*\\1"),
                    false,
                    true
                )
            ),
            token(
                "property",
                pattern(
                    Pattern.compile(
                        "[-_a-z\\xA0-\\uFFFF][-\\w\\xA0-\\uFFFF]*(?=\\s*:)",
                        Pattern.CASE_INSENSITIVE
                    )
                )
            ),
            token(
                "important",
                pattern(Pattern.compile("\\B!important\\b", Pattern.CASE_INSENSITIVE))
            ),
            token(
                "function",
                pattern(Pattern.compile("[-a-z0-9]+(?=\\()", Pattern.CASE_INSENSITIVE))
            ),
            token("punctuation", pattern(Pattern.compile("[(){};:]")))
        )

        // can we maybe add some helper to specify simplified location?

        // now we need to put the all tokens from grammar inside `atrule` (except the `atrule` of cause)
        val atrule = grammar.tokens().get(1)
        val inside: Prism4j.Grammar? = GrammarUtils.findFirstInsideGrammar(atrule)
        if (inside != null) {
            for (token in grammar.tokens()) {
                if ("atrule" != token.name()) {
                    inside.tokens().add(token)
                }
            }
        }

        val markup: Prism4j.Grammar? = prism4j.grammar("markup")
        if (markup != null) {
            GrammarUtils.insertBeforeToken(
                markup, "tag",
                token(
                    "style",
                    pattern(
                        Pattern.compile(
                            "(<style[\\s\\S]*?>)[\\s\\S]*?(?=<\\/style>)",
                            Pattern.CASE_INSENSITIVE
                        ),
                        true,
                        true,
                        "language-css",
                        grammar
                    )
                )
            )

            // important thing here is to clone found grammar
            // otherwise we will have stackoverflow (inside tag references style-attr, which
            // references inside tag, etc)
            val markupTagInside: Prism4j.Grammar?
            run {
                var _temp: Prism4j.Grammar? = null
                val token: Prism4j.Token? = GrammarUtils.findToken(markup, "tag")
                if (token != null) {
                    _temp = GrammarUtils.findFirstInsideGrammar(token)
                    if (_temp != null) {
                        _temp = GrammarUtils.clone(_temp)
                    }
                }
                markupTagInside = _temp
            }

            GrammarUtils.insertBeforeToken(
                markup, "tag/attr-value",
                token(
                    "style-attr",
                    pattern(
                        Pattern.compile(
                            "\\s*style=(\"|')(?:\\\\[\\s\\S]|(?!\\1)[^\\\\])*\\1",
                            Pattern.CASE_INSENSITIVE
                        ),
                        false,
                        false,
                        "language-css",
                        grammar(
                            "inside",
                            token(
                                "attr-name",
                                pattern(
                                    Pattern.compile(
                                        "^\\s*style",
                                        Pattern.CASE_INSENSITIVE
                                    ),
                                    false,
                                    false,
                                    null,
                                    markupTagInside
                                )
                            ),
                            token(
                                "punctuation",
                                pattern(Pattern.compile("^\\s*=\\s*['\"]|['\"]\\s*$"))
                            ),
                            token(
                                "attr-value",
                                pattern(
                                    Pattern.compile(".+", Pattern.CASE_INSENSITIVE),
                                    false,
                                    false,
                                    null,
                                    grammar
                                )
                            )

                        )
                    )
                )
            )
        }

        return grammar
    }
}
